package lxs.swift.tool;


import lxs.swift.fn.Func;
import lxs.swift.tool.acc.Acc;
import lxs.swift.tool.acc.Port;

import java.lang.invoke.SerializedLambda;
import java.util.*;
import java.util.function.Function;

/**
 * 排序工具
 * <pre><code>
 *  Sorter<User> userSorter = Sorter.create(User::age, User::name, User::isMale);//定义排序字段
 *  Sorter.Rank<User> rank = userSorter.rank("age:desc", "name:asc");
 *  //  Sorter.Rank<User> rank = userSorter.rank(Sorter.desc(User::age), User::name);
 *  List<User> users = users();
 *  rank.sort(users);
 *  System.out.println(users);
 * </code></pre>
 *
 * @param <T>
 */
public class Sorter<T> {

    private final Map<String, Get<T, ?>> nameMapGet = new HashMap<>();
    private final Acc<Get<T, ?>, Comparator<T>, Comparator<T>> acc = Acc.refreshLazy(Get::comparator, (get, cm) -> cm.thenComparing(get.comparator()), c -> c);

    private Sorter() {
    }

    @SafeVarargs
    public static <T> Sorter<T> create(Get<T, ?>... gets) {
        Sorter<T> sorter = new Sorter<>();
        for (Get<T, ?> get : gets) {
            SerializedLambda lambda = Lambdas.resolve(get);
            String name = Lambdas.toName(lambda);
            sorter.nameMapGet.put(name, get);
        }
        return sorter;
    }

    public static <T, F extends Comparable<F>> Get<T, F> asc(Get<T, F> get) {
        return get;
    }

    public static <T, F extends Comparable<F>> Get<T, F> desc(Get<T, F> get) {
        return get.rev();
    }

    public Rank<T> rank(Collection<String> items) {
        Port<Get<T, ?>, Comparator<T>> port = Port.bind(acc);
        for (String item : items) {
            String trim = item.trim();
            if (trim.isEmpty()) continue;
            String[] split = trim.split(":");
            if (split.length != 2) {
                throw new RuntimeException("Invalid sorter definition: " + trim);
            }
            String order = split[0].trim();
            String sort = split[1].trim();
            Get<T, ?> tGet = this.nameMapGet.get(order);
            if (tGet == null) {
                throw new RuntimeException("Sorter does not contain an order: " + order);
            }
            if ("desc".equals(sort)) tGet = tGet.rev();
            port.push(tGet);
        }
        Comparator<T> comparator = port.pull();
        return list -> list.sort(comparator);
    }

    public Rank<T> rank(String... items) {
        return rank(Arrays.asList(items));
    }

    @SafeVarargs
    public final Rank<T> rank(Get<T, ?>... gets) {
        Port<Get<T, ?>, Comparator<T>> port = Port.bind(acc);
        for (Get<T, ?> get : gets) {
            port.push(get);
        }
        Comparator<T> comparator = port.pull();
        return list -> list.sort(comparator);
    }


    public interface Rank<T> {
        void sort(List<T> list);

        default List<T> sort(Collection<T> collection) {
            ArrayList<T> list = new ArrayList<>(collection);
            sort(list);
            return list;
        }
    }

    @FunctionalInterface
    public interface Get<T, F> extends Func<T, F, RuntimeException> {

        default int compare(T o1, T o2) {
            F f1 = apply(o1);
            F f2 = apply(o2);
            if (f1 == null && f2 == null) {
                return 0;
            }
            if (f1 == null || f2 == null) return -1;
            //F 不能直接定义为实现了Comparable,不然会在多参数方法中语法报错
            if (!(f1 instanceof Comparable && f2 instanceof Comparable)) {
                throw new RuntimeException("Sorter does not support comparing " + f1 + " and " + f2);
            }
            return ((Comparable<F>) f1).compareTo(f2);
        }

        default Get<T, F> rev() {
            Get<T, F> get = this;
            return new Get<T, F>() {

                @Override
                public F apply(T t) {
                    return get.apply(t);
                }

                @Override
                public int compare(T o1, T o2) {
                    return Get.super.compare(o2, o1);
                }
            };
        }

        default Comparator<T> comparator() {
            return this::compare;
        }


    }
}
